package cmd

import (
	"context"
	"fmt"
	"log"
	"log/slog"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"golang.org/x/text/cases"
	"golang.org/x/text/language"

	"gitee.com/rogax/kagent/pkg/cron"
	"gitee.com/rogax/kagent/pkg/dmi"
	"gitee.com/rogax/kagent/pkg/ipmi"
	"gitee.com/rogax/kagent/pkg/lldp"
	"gitee.com/rogax/kagent/pkg/osinfo"
	"gitee.com/rogax/kagent/pkg/types"
	"gitee.com/rogax/kagent/version"
	"github.com/spf13/cobra"
)

//go:generate echo "package main; var Version = \"1.0.0\"" > version.go
const (
	// APPNAME is the name of this application
	APPNAME string = "kagent" // 应用名称
	// VERSION is the version of the program
	// VERSION string = "v0.0.1" // 应用版本

)

// type APP struct {
// 	// TlsCert string
// 	// TlsKey  string
// 	Ctx         context.Context
// 	Port        int
// 	Server      string
// 	RemoteURL   string
// 	Duration    time.Duration
// 	Debug       bool
// 	Logger      *log.Logger
// 	Slogger     *slog.Logger
// 	DmiPath     string
// 	ProcPath    string
// 	IPMIDevPath string
// 	SysRoot     string
// 	SysPath     string
// }

var (
	// tlsCert string
	// tlsKey  string
	port      int
	server    string
	remoteURL string
	duration  time.Duration
	debug     bool
	// // codecs  = serializer.NewCodecFactory(runtime.NewScheme())
	// logger      = log.New(os.Stdout, "", log.LstdFlags)
	dmiPath     string
	ipmiDevPath string
	sysRoot     string
	ctx, cancel = context.WithCancel(context.Background())
	slogger     = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: true}))
)

var rootCmd = &cobra.Command{
	Use:   APPNAME,
	Short: "Kubernetes mutating webhook example",
	Long: `Example showing how to implement a basic mutating webhook in Kubernetes.

Example:
$ kagent --tls-cert <tls_cert> --tls-key <tls_key> --port <port> --server <host_url>`,
	Run: func(cmd *cobra.Command, args []string) {
		// if tlsCert == "" || tlsKey == "" {
		// 	fmt.Println("--tls-cert and --tls-key required")
		// 	os.Exit(1)
		// }
		run()
	},
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	cobra.CheckErr(rootCmd.Execute())
}

func init() {
	// rootCmd.Flags().StringVar(&tlsCert, "tls-cert", "", "Certificate for TLS")
	// rootCmd.Flags().StringVar(&tlsKey, "tls-key", "", "Private key file for TLS")
	rootCmd.Flags().StringVar(&server, "server", "127.0.0.1", "Connect to server")
	rootCmd.Flags().StringVar(&remoteURL, "remote-server", "", "Connect to remote server")
	rootCmd.Flags().IntVar(&port, "port", 9095, "Port to listen on for HTTP traffic")
	rootCmd.Flags().DurationVar(&duration, "duration", 30*time.Second, "Duration for which to sleep")
	rootCmd.Flags().BoolVar(&debug, "debug", false, "Enable debug logging")
	rootCmd.Flags().StringVar(&dmiPath, "dmi-path", "/sys/class/dmi/id", "Read information from device.")
	rootCmd.Flags().StringVar(&ipmiDevPath, "ipmi-dev-path", "/dev/ipmi0", "Read information from device.")
	rootCmd.Flags().StringVar(&sysRoot, "sys-root", "/", "Read information from device.")
}

func NewAPPCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   APPNAME,
		Short: "Kubernetes mutating webhook example",
		Long:  `Example showing how to implement a basic mutating webhook in Kubernetes.`,
	}
	return cmd
}

func run() {
	// 初始化日志
	logger := log.New(os.Stdout, "", log.LstdFlags)
	// logger.Println("Starting", APPNAME, VERSION, version.GitCommit, version.BuildDate)
	fmt.Println(version.String())

	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	go func() {
		sigchan := make(chan os.Signal, 1)
		signal.Notify(sigchan, os.Interrupt, syscall.SIGTERM)
		<-sigchan
		cancel()
		logger.Println("Received SIGINT signal. Canceling context...")
	}()

	app := &types.APP{
		// TlsCert: tlsCert,
		// TlsKey:  tlsKey,
		Ctx:         ctx,
		Port:        port,
		Server:      server,
		RemoteURL:   remoteURL,
		Duration:    duration,
		Debug:       debug,
		Logger:      logger,
		Slogger:     slogger,
		SysRoot:     &sysRoot,
		DmiPath:     sysRoot + dmiPath,
		IPMIDevPath: ipmiDevPath,
		SysPath:     filepath.Join(sysRoot, "/sys"),
		ProcPath:    filepath.Join(sysRoot, "/proc"),
	}

	s := &http.Server{
		Addr: fmt.Sprintf("%s:%d", app.Server, app.Port),
		//Handler:        myHandler,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	lldp0 := lldp.New(ctx)
	// lldp0.Rund()
	dmi0 := dmi.New(&app.SysPath, &app.DmiPath)
	osinfo0 := osinfo.New(app.SysRoot)
	ipmi0 := ipmi.New(app.IPMIDevPath)

	if remoteURL != "" {
		var cronServices []types.CronService

		_, err := os.Stat(lldp0.Socket)
		if os.IsNotExist(err) {
			logger.Println("lldpd not running")
		} else {
			cronServices = append(cronServices, lldp0)
		}

		cronServices = append(cronServices, dmi0, osinfo0, ipmi0)
		go cron.Cron(
			app.Ctx, app.Duration, app.RemoteURL,
			cronServices,
		)

	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(cases.Title(language.Und, cases.NoLower).String(APPNAME)))
	})
	http.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(version.String()))
	})
	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("ok"))
	})
	http.HandleFunc("/lldp", lldp0.ServeHTTP)
	http.HandleFunc("/dmi", dmi0.ServeHTTP)
	http.HandleFunc("/osinfo", osinfo0.ServeHTTP)
	http.HandleFunc("/ipmi", ipmi0.ServeHTTP)

	// 启动http服务器
	go func() {
		logger.Printf("Listening on %s:%d\n", app.Server, app.Port)
		logger.Fatal(s.ListenAndServe())
		// if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		// 	log.Fatal(err)
		// }
	}()

	// 关闭http服务器
	<-ctx.Done()
	logger.Println("Shutting down server...")
	s.Shutdown(ctx)
}
